<?php

Raise::load('core.RaiseStaticClass');
Raise::load('core.RaiseVariable');
Raise::load('core.RaiseCollection');
Raise::load('core.RaiseHtmlTag');

/**
 * RaiseText helper class
 * Helps do perform text manipulation such as linkifying, excerpting, highlighting, strip tags so on
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.Text
 * @since 1.2
 */
class RaiseText extends RaiseStaticClass {

    /**
     * An array of line breaking points
     * @var array
     * @static
     */
    private static $linebreaks = array(' ', '-', '.', ';', ')', ']');

    /**
     * Newline in Windows
     */
    const NEWLINE_WIN = "\r\n";

    /**
     * Newline in Macintosh
     */
    const NEWLINE_MAC = "\r";

    /**
     * Newline in *nix
     */
    const NEWLINE_NIX = "\n";

    /**
     * Replace all kind of new lines in a string to a standard newline character
     * @param string $s The string to process
     * @param string $nl The newline character to use
     * @return string The string after processing
     */
    public static function neutralizeNewlines($s, $nl = self::NEWLINE_NIX) {
        static $token = '$@MEWH3REN3WL!NE';
        return str_replace(array(self::NEWLINE_WIN, self::NEWLINE_NIX, self::NEWLINE_MAC, $token), array($token, $token, $token, $nl), $s);
    }

    /**
     * Truncate string if it is longer than $length
     * Note that if the breaking occurs  in a middle of a word, the method will find a line breaking character to break the word
     * @param string $string The original string
     * @param integer $length The maximum length of the
     * @param string $ending (optional) The ending to append if length of string is too long. Defaults to '...'
     * @return string
     */
    public static function truncate($string, $length, $ending = '...') {
        if (strlen($string) < $length) {
            return $string;
        }
        //$before = substr($string, $length - 1, 1);
        $that = substr($string, $length, 1);
        //$after = substr($string, $length + 1, 1);
        if (!in_array($that, self::$linebreaks)) {
            $i = $length;
            while ($i-- > 0) {
                $c = $string[$i];
                if (in_array($c, self::$linebreaks)) {
                    break;
                }
            }
            $length = $i;
        }
        return substr($string, 0, $length) . $ending;
    }

    /**
     * Highlight specific phrase in a string case-insensitively
     * @param string $string The original string to search in
     * @param string|RaiseCollection|array $phrase The phrase to highlight
     * @param string $format (optional) The format of the highlight. Note that $1 refers to the original text to be highlighted. Defaults to '<b>$1</b>'
     * @return string
     */
    public static function highlight($string, $phrase, $format = '<b>$1</b>') {
        if ($phrase instanceof RaiseCollection || RaiseVariable::isType($phrase, RaiseVariable::TYPEARRAY)) {
            foreach ($phrase as &$p) {
                $p = '`(?![^<]+>)(' . $p . ')(?![^<]+>)`is';
            }
        } else {
            $phrase = '`(?![^<]+>)(' . $phrase . ')(?![^<]+>)`is';
        }
        return preg_replace($phrase, $format, $string);
    }

    /**
     * Strip off all HTML tags in a string
     * @param string $str The string to strip HTML tags
     * @param string $allowed (optional) The tags to be allowed
     * @return string
     * @link http://php.net/strip-tags
     */
    public static function stripTags($str, $allowed = null) {
        return strip_tags($str, $allowed);
    }

    /**
     * Automatically detect and add links (URL and Email) to a string
     * @param string $string The string to linkify
     * @param RaiseHtmlTag $tag (optional) The tag to format the URLs
     * @param boolean $url (optional) Sets whether URLs are linkified. Defaults to TRUE.
     * @param boolean $email (optional) Sets wsaahether Emails are linkified. Defaults to TRUE.
     * @return string The final string
     */
    public static function linkify($s, $tag = null, $url = true, $email = true) {
        if (!($tag instanceof RaiseHtmlTag)) {
            $tag = new RaiseHtmlTag('a', '\'.$matches[0].\'');
        }
        if (!$tag->value()) {
            $tag->value('\'.$matches[0].\'');
        }
        if ($url) {
            $tag->attributes()->add('href', '\'.$matches[0].\'');
            $s = preg_replace_callback('`(?<!href="|">)((?:https?|ftp|nntp)://[^\s<>()]+)`i', create_function('$matches', 'return \'' . $tag->html() . '\';'), $s);

            $tag->attributes()->add('href', 'http://\'.$matches[0].\'');
            $s = preg_replace_callback('`(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))`i', create_function('$matches', 'return \'' . $tag->html() . '\';'), $s);
        }
        if ($email) {
            $tag->attributes()->add('href', 'mailto:\'.$matches[0].\'');
            $s = preg_replace_callback('`([_A-Za-z0-9+-]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)`i', create_function('$matches', 'return \'' . $tag->html() . '\';'), $s);
        }
        return $s;
    }

    /**
     * Create a comma separated list where the last two items are joined with 'and', forming natural English
     * @param array|RaiseCollection $list The array or collection
     * @param string $and (optional) The 'and' word to use. Defaults to 'and'.
     * @param string $separator (optional) The comma separator to use. Defaults to ', '
     * @return string
     */
    public static function listing($list, $and = 'and', $separator = ', ') {
        if (count($list) > 1) {
            return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
        } else {
            return array_pop($list);
        }
    }

    /**
     * Rotate the string through 13 characters
     * @param RaiseString|string $s
     * @return RaiseString|string 
     */
    public static function rotate13($s) {
        return str_rot13($s);
    }

    /**
     * Rotate the string through 47 characters
     * @param RaiseString|string $s
     * @return RaiseString|string
     */
    public static function rotate47($s) {
        return strtr($s, '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', 'PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO');
    }

}
